import{
    BigNumberish,
    BigNumber,
    CallOverrides,
    providers,
    Signer
} from "ethers";
import { ERC721, ERC721__factory } from "../../types";
import { IERC721Client } from "./IERC721-client";


export class EtherERC721Client implements IERC721Client{

    public erc721: ERC721 | undefined;//typechain 合约
    public provider: providers.Provider | undefined;
    public signer: Signer | undefined;
    public _waitConfirmations = 5;

    public connectProvider(address: string, provider: providers.Provider): void{
        this.provider = provider;
        this.erc721 = ERC721__factory.connect(address,this.provider);
    }

    public connectSigner(signer: Signer): void{
        this.signer = signer;
    }

    public setWaitConfirmations(num: number): void{
        this._waitConfirmations = num;
    }

    public contract(): Promise<ERC721> {
        if (this.provider === undefined || this.erc721 === undefined) {
          return Promise.reject('need to connect a valid provider');
        }
        return Promise.resolve(this.erc721);
    }

    public async approve(
        to: string,
        tokenId: BigNumberish,
        config ?:CallOverrides
      ): Promise<any>
    {
        if (
            this.provider === undefined ||
            this.erc721 === undefined ||
            this.signer === undefined
        ){
            return Promise.reject('need to connect a valid provider and signer');
        }

        const gas = await this.erc721
        .connect(this.signer)
        .estimateGas.approve(to,tokenId, { ...config });

        const transaction = await this.erc721
        .connect(this.signer)
        .approve(to,tokenId, { gasLimit: gas.mul(13).div(10), ...config });
        
        const receipt = await transaction.wait(this._waitConfirmations);
        return receipt;
    }
  
    public balanceOf(owner: string, config ?:CallOverrides): Promise<BigNumber>{
        if (
            this.provider === undefined ||
            this.erc721 === undefined 
        ){
            return Promise.reject('need to connect a valid provider');
        }

        return this.erc721.balanceOf(owner,{...config});
    }
  
    public getApproved(
        tokenId: BigNumberish,
        config ?:CallOverrides
    ): Promise<string>{
        if (
            this.provider === undefined ||
            this.erc721 === undefined 
        ){
            return Promise.reject('need to connect a valid provider');
        }

        return this.erc721.getApproved(tokenId,{...config});
    }
  
    public isApprovedForAll(
        owner: string,
        operator: string,
        config ?:CallOverrides
    ): Promise<boolean>{
        if (
            this.provider === undefined ||
            this.erc721 === undefined 
        ){
            return Promise.reject('need to connect a valid provider');
        }

        return this.erc721.isApprovedForAll(owner,operator,{...config})
    }
  
    public name(config ?:CallOverrides): Promise<string>{
        if (
            this.provider === undefined ||
            this.erc721 === undefined 
        ){
            return Promise.reject('need to connect a valid provider');
        }

        return this.erc721.name({...config});
    }
  
    public ownerOf(
        tokenId: BigNumberish,
        config ?:CallOverrides
    ): Promise<string>
    {
        if (
            this.provider === undefined ||
            this.erc721 === undefined 
        ){
            return Promise.reject('need to connect a valid provider');
        }

        return this.erc721.ownerOf(tokenId,{...config});
    }
  
    //   "safeTransferFrom(address,address,uint256)"(
    //     from: string,
    //     to: string,
    //     tokenId: BigNumberish,
    //     config ?:CallOverrides
    //   ): Promise<any>;
  
    //   "safeTransferFrom(address,address,uint256,bytes)"(
    //     from: string,
    //     to: string,
    //     tokenId: BigNumberish,
    //     _data: BytesLike,
    //     config ?:CallOverrides
    //   ): Promise<any>;
  
    public async setApprovalForAll(
        operator: string,
        approved: boolean,
        config ?:CallOverrides
    ): Promise<any>{
        if (
            this.provider === undefined ||
            this.erc721 === undefined ||
            this.signer === undefined
        ){
            return Promise.reject('need to connect a valid provider and signer');
        }

        const gas = await this.erc721
        .connect(this.signer)
        .estimateGas.setApprovalForAll(operator,approved,{...config});
    

        const transaction = await this.erc721
        .connect(this.signer)
        .setApprovalForAll(operator,approved,{ gasLimit: gas.mul(13).div(10), ...config });
        
        const receipt = await transaction.wait(this._waitConfirmations);
        return receipt
    }
  
    
        
    public symbol(config?: CallOverrides): Promise<string>{
        if (
            this.provider === undefined ||
            this.erc721 === undefined 
        ){
            return Promise.reject('need to connect a valid provider');
        }

        return this.erc721?.symbol({...config});
    }
  
    public tokenURI(
        tokenId: BigNumberish,
        config ?:CallOverrides
    ): Promise<string>{
        if (
            this.provider === undefined ||
            this.erc721 === undefined 
        ){
            return Promise.reject('need to connect a valid provider');
        }
        return this.erc721.tokenURI(tokenId,{...config});
    }
  
    public async transferFrom(
        from: string,
        to: string,
        tokenId: BigNumberish,
        config ?:CallOverrides
    ): Promise<any>{
        if (
            this.provider === undefined ||
            this.erc721 === undefined ||
            this.signer === undefined
        ){
            return Promise.reject('need to connect a valid provider and signer');
        }

        const gas = await this.erc721
        .connect(this.signer)
        .estimateGas.transferFrom(from,to,tokenId,{...config});
    

        const transaction = await this.erc721
        .connect(this.signer)
        .transferFrom(from,to,tokenId,{ gasLimit: gas.mul(13).div(10), ...config });
        
        const receipt = await transaction.wait(this._waitConfirmations);
        return receipt;
    }
}